package com.googlecode.project4uz.decoder;

import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Iterator;

import org.openrdf.model.Statement;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.RDFParser;
import org.openrdf.rio.Rio;
import org.openrdf.rio.helpers.StatementCollector;

/**
 * Wykorzystanie biblioteki Rio do parsowania plikow RDF.
 * 
 * @author Piotr Broniszewski
 * @author Artur Gertych
 * @author Daniel Kur
 */
public class RioReader
{
	private RDFParser rdf;
	private StatementCollector collector;
	private ObjectWriter objectWriter;
	
	/**
	 * Tworzy obiekt <i>reader'a</i> obiektow z formatu RDF.
	 * @param format - format RDF jakim zapisujemy do strumienia;
	 * @param output - strumien wejsciowy.
	 * @throws RDFParseException
	 * @throws RDFHandlerException
	 * @throws IOException
	 * @throws NumberFormatException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchFieldException
	 */
	public RioReader(RDFFormat format, InputStream input) throws RDFParseException, RDFHandlerException, IOException, NumberFormatException, SecurityException, IllegalArgumentException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException
	{
		this.objectWriter = new ObjectWriter();
		this.collector = new StatementCollector();
		this.rdf = Rio.createParser(format);
		this.rdf.setRDFHandler(collector );
		this.rdf.parse(input, "");
			
		Collection<Statement> col = collector.getStatements();
		Iterator<Statement> it = col.iterator();
			
		while (it.hasNext())
			readObject(it.next());
	}
	
	/**
	 * Odczytuje ze strumienia wejsciowego obiekty.
	 * @param stmt - wyrazenie (statement).
	 * @throws NotSerializableException
	 * @throws NumberFormatException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchFieldException
	 */
	public void readObject(Statement stmt) throws NotSerializableException, NumberFormatException, SecurityException, IllegalArgumentException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException
	{
		System.out.println(stmt);
		
		if (stmt.getPredicate().toString().startsWith("object:/"))
		{
			objectWriter.setObject(stmt.getObject().stringValue());
			System.out.println("Utworzylem obiekt: " + stmt.getPredicate().getLocalName());
		}
		
		else if (stmt.getPredicate().getNamespace().equals("serial:/"))
		{
			if (!objectWriter.isSerializable(stmt.getSubject().stringValue(), stmt.getObject().stringValue()))
				throw new NotSerializableException("Nie moge deserializowac obiektu! Niezgodnosc wersji!");
		}
		
		else if (isSimpleType(stmt.getPredicate().getNamespace()) && !(stmt.getSubject().toString().contains("java.util.LinkedList") || 
				stmt.getSubject().toString().contains("java.util.ArrayList") || stmt.getSubject().toString().contains("java.util.Vector") ||
				stmt.getSubject().toString().contains("java.util.Vector") || stmt.getSubject().toString().contains("java.util.HashMap")))
		{
			objectWriter.setPrimitive(stmt.getSubject().stringValue(), stmt.getPredicate().getLocalName(), stmt.getObject().stringValue());
			System.out.println("Zainicjalizowalem: " + stmt.getPredicate().getLocalName());
		}
		
		else if (stmt.getPredicate().getNamespace().toString().equals("array:/"))
		{
			objectWriter.setArray(stmt.getSubject().stringValue(), stmt.getPredicate().getLocalName(), stmt.getObject().stringValue(), parseArraySize (stmt.getObject().stringValue()));
			System.out.println("Utworzylem tablice: " + stmt.getPredicate().getLocalName());
		}
		
		else if (stmt.getPredicate().getNamespace().toString().equals("collection:/"))
		{
			objectWriter.setCollection(stmt.getSubject().stringValue(), stmt.getObject().stringValue(), stmt.getPredicate().getLocalName(), parseCollectionType(stmt.getObject().stringValue()));
			System.out.println("Utworzylem kolekcje: " + stmt.getPredicate().getLocalName());
		}
		
		else if (stmt.getSubject().toString().contains("java.util.LinkedList") || stmt.getSubject().toString().contains("java.util.ArrayList") ||
				stmt.getSubject().toString().contains("java.util.HashSet") || stmt.getSubject().toString().contains("java.util.Vector"))
		{
			if (!stmt.getPredicate().getNamespace().equals("reference:/"))
			{
				objectWriter.setPrimitiveInCollection(stmt.getSubject().stringValue(), stmt.getObject().stringValue());
				System.out.println("Zainicjalizowalem: " + stmt.getPredicate().getLocalName() + " w kolekcji");
			}
			else
			{
				objectWriter.setReferenceInCollection(stmt.getSubject().stringValue(), stmt.getObject().stringValue(), stmt.getPredicate().getLocalName());
				System.out.println("Utworzylem referencje: " + stmt.getPredicate().getLocalName() + " w kolekcji");
			}
		}
		
		else if (stmt.getPredicate().getNamespace().toString().equals("map:/"))
		{
			objectWriter.setMap(stmt.getSubject().stringValue(), stmt.getObject().stringValue(), stmt.getPredicate().getLocalName(), parseCollectionType(stmt.getObject().stringValue()));
			System.out.println("Utworzylem mape: " + stmt.getPredicate().getLocalName());
		}
		
		else if (stmt.getSubject().toString().contains("java.util.Map") || stmt.getSubject().toString().contains("java.util.HashMap"))
		{
			objectWriter.setPrimitiveInMap(stmt.getSubject().stringValue(), stmt.getObject().stringValue(), stmt.getPredicate().getNamespace(), stmt.getPredicate().getLocalName());
			System.out.println("Zainicjalizowalem: " + stmt.getPredicate().getLocalName() + " w mapie");
		}
		
		else if (stmt.getPredicate().getNamespace().equals("reference:/"))
		{
			if (!stmt.getObject().stringValue().equals("rdf:/null"))
				objectWriter.setReference(stmt.getSubject().stringValue(), stmt.getPredicate().getLocalName(), stmt.getObject().stringValue());
			System.out.println("Utworzylem referencje: " + stmt.getPredicate().getLocalName());
		}
	}
	
	/**
	 * Sprawdza, czy odczytany obiekt jest typem prostym.
	 * @param ns - wartosc predykatu z wyrazenia.
	 * @return <i>true</i>, jezeli obiekt jest typem prostym
	 * lub <i>false</i>, jezeli obiekt nie jest typem prostym.
	 */
	private final static boolean isSimpleType(final String ns)
	{
		if (ns.equals("boolean:/")	|| ns.equals("byte:/")	|| ns.equals("char:/")	||
			ns.equals("short:/")	|| ns.equals("int:/")	|| ns.equals("long:/")	|| 
			ns.equals("float:/")	|| ns.equals("double:/")|| ns.equals("String:/")) return true;
		return false;
	}
	
	/**
	 * Pobiera rozmiar tablicy.
	 * @param objectURI - wartosc obiktu z wyrazenia.
	 * @return rozmiar tablicy.
	 */
	final static int parseArraySize(final String objectURI)
	{
		int i = objectURI.length() - 1;
		while (objectURI.charAt(i) != 'S') i--;
		return Integer.parseInt(objectURI.substring(++i));
	}
	
	/**
	 * Pobiera typ kolekcji.
	 * @param objectURI - wartosc obiektu z wyrazenia.
	 * @return typ kolekcji.
	 */
	final static String parseCollectionType(final String objectURI)
	{
		int i = 5;
		StringBuilder sbCollectionType = new StringBuilder();
		
		while (objectURI.charAt(i) != ',')
		{
			sbCollectionType.append(objectURI.charAt(i));
			i++;
		}
		
		return sbCollectionType.toString();
	}
	
	/**
	 * Pobiera typ pola kolekcji w dlugim formacie.
	 * @param objectURI - wartosc obiektu z wyrazenia;
	 * @param isValue - okresla, czy pole jest kluczem, czy wartoscia.
	 * @return typ kolekcji w dlugim formacie.
	 */
	final static String parseCollectionFieldType(final String objectURI, boolean isValue)
	{
		int i = 5;
		StringBuilder sbFieldType = new StringBuilder();
		
		while (objectURI.charAt(i) != ',')
		{
			i++;
		}
		i++;
		
		if (!isValue)
		{
			while (objectURI.charAt(i) != ',')
			{
				sbFieldType.append(objectURI.charAt(i));
				i++;
			}
		}
		
		else if (isValue)
		{
			while (objectURI.charAt(i) != '#')
			{
				sbFieldType.append(objectURI.charAt(i));
				i++;
			}
		}
		
		return sbFieldType.toString();
	}
	
	/**
	 * Pobiera typ pola kolekcji w krotkim formacie.
	 * @param objectURI - wartosc obiektu z wyrazenia;
	 * @param isValue - okresla, czy pole jest kluczem, czy wartoscia.
	 * @return typ kolekcji w krotkim formacie.
	 */
	final static String parseCollectionFieldTypeShort(final String objectURI, boolean isValue)
	{
		String longFormat = parseCollectionFieldType(objectURI, isValue);
		int i = longFormat.length()-1;
		StringBuilder sbFieldType = new StringBuilder();
		
		while (longFormat.charAt(i) != '.')
		{
			sbFieldType.append(longFormat.charAt(i));
			i--;
		}
		String s = new String(sbFieldType.reverse());
		
		return s;
	}
	
	/**
	 * Pobiera zdeserializowany obiekt.
	 * @return zdeserializowany obiekt.
	 */
	public Object readObject()
	{ 
		return objectWriter.readObject();
	}
}
